Fork me on GitHub

Python 高级提升(一)

前言:

  1. GIL (全局解释器锁);
  2. 赋值、浅拷贝、深拷贝;
  3. import 导入模块;
  4. 多继承以及 MRO 顺序。

一、GIL (全局解释器锁)

1. 单线程、多线程、多进程执行分析
双核 cpu
- 单线程: 一个 cpu 100% 使用,另一个空闲
- 两个线程: 两个 cpu 各自使用 50%
- 两个进程: 两个 cpu 各自使用 100%

2. GIL 概念:
GIL,全局解释器锁(global interpreter lock),它不是 python 语言的特性,而是 python 默认的解析器 cpython 的特性
cpython 要求每个线程必须先获取 GIL 锁,才能执行线程中的代码

目的:解决多线程同时竞争解析器程序的全局变量而出现的线程安全问题
不足:在多线程中不能充分利用多核 cpu

3. 如何解决 GIL 问题:
1. 换解析器 ,把 cpython 换成其他的,比如 jpython
2. 针对多线程执行的业务,用其他语言代码,比如 c++,java 来代替,python 就是胶水语言
3. 多进程+多协程方案

4. 面试题:
描述 Python GIL 的概念,以及它对 python 多线程的影响?一个单线程抓取网页的程序,与一个多线程抓取网页的程序哪个性能更高,并解释原因。

1. GIL,全局解释器锁(global interpreter lock),它是 cpython 解析器的特性,不是 python 的特性 ,它要求线程在执行前,需要获取GIL锁,
2. 由于 GIL 的存在,会影响多线程不能利用多核 CPU 资源,通过多进程方式可利用多个 CPU 资源
3. 线程释放 GIL 锁的情况:
在 IO 操作等可能会引起阻塞的 system call 之前,可以暂时释放 GIL,但在执行完毕后,必须重新获取 GIL
Python 3.x 使用计时器(执行时间达到阈值后,当前线程释放 GIL)
4. 多线程爬取比单线程性能有提升,因为遇到 IO 阻塞会自动释放 GIL 锁,这样在线程阻塞情况下,可以执行其他线程中的代码

二、赋值、浅拷贝、深拷贝

赋值是对多个对象指向同一个内存空间,多个对象共同操作同一个数据
深拷贝与浅拷贝是对内存数据的复制,目的是能够单独操作数据

1. 赋值
对象之间赋值本质上是对象之间内存地址的引用传递
也就是多个对象指向同一个内存空间

2. 浅拷贝
1. 引入 copy 模块
2. copy.copy(object)
object: 要拷贝的对象

小结:浅拷贝是对象的第一层(顶层)的拷贝

3. 深拷贝
深拷贝
1. 引入 copy 模块
2. copy.deepcopy(object)
object: 要拷贝的对象

小结:深拷贝是对象的所有层的拷贝

4. 不可变类型(元组,数字,字符串)的拷贝
1> 如果在多层嵌套中都是不可变类型
浅拷贝与深拷贝都是相同的,都不会单独开辟内存空间,而是引用原来的内存空间
2> 如果顶层是不可变类型,但内部元素有嵌入可变类型
浅拷贝还是不会开辟新的内存空间,而是引用原来的内存空间
深拷贝会拷贝对象的所有层

5. 其他拷贝方式(列表切片、字典中的 copy)
列表切片、字典中的 copy 都使用浅拷贝


面试题:
如何在内存中复制一个数据
浅拷贝,深拷贝 copy 模块
切片操作与字典中的拷贝属于哪种拷贝:
浅拷贝

三、import 导入模块

1. import 搜索路径
a> 查看搜索路径
sys.path # 系统预设的搜索路径

b> 设置搜索路径
sys.path.append("/python/a")
sys.path.insert(0,"/python/b")

2. 重新导入模块
热更新: 服务没有停止,再更新模块
import imp
imp.reload(load_test)
load_test:模块名

3. 多模块开发时引用其它模块共享变量的问题
1. import module 方式
本地不会创建新变量,操作的是目标模块的变量
2. from module import * 方式
在本地创建一个与目标模块相同的变量名,并且与目标模块变量指向同一个内存空间

面试题: import modulefrom module import * 两种模块导入有何区别

1. import module 引用共享变量时,要使用 module.变量名,而 from module import * 直接使用变量名即可
2. import module 方式
本地不会创建新变量,操作的是目标模块的变量
3. from module import * 方式
在本地创建一个与目标模块相同的变量名,并且与目标模块变量指向同一个内存空间

四、多继承以及 MRO 顺序

1. 单继承
子类调用父类的方法的方式:

1. super().__init__()
2. Parent.__init__(self)
3. super(类名,self).__init__()

单继承中,使用父类名与super调用父类的方法没有差异,是一样的

2. 多继承中使用父类名调用父类的方法
出现问题: 父类的方法会调用多次

3. 多继承中使用 super() 调用父类的方法
在多继承中,建议使用 super() 来调用父类的方法,而不用父类名的方式

- mro顺序: Method Resolution Order :
方法解决顺序,方法解析顺序
用于描述在Python多继承中子类调用多个父类相同方法的顺序
内部通过C3算法去解析
- 类名.__mro__ : 用元组的形式存放子类调用多个父类相同方法的顺序(mro算法执行的结果)

- super()与类名.__mro__的关系:
# (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)

super(类名,self)就是依照这个顺序确定要调用的是哪个父类的方法
通过self确定mro顺序
通过类名找到当前类在mro中的位置,再返回下一个类作为super调用的父类

小结:
1. 单继承中,super().__init__相对于类名.__init__,基本无差别
2. 多继承中,super() 能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次
多继承中建议使用super()
2. super() 内部是通过 mro 算法来确定调用哪个父类的方法

五、其他

1. vim -On one.py  two.py 
在vim中分屏显示多个文件
2.Htop命令:
显示的内容比top命令更加丰富,用于监视进程的资源占用率的情况
-------------本文结束感谢您的阅读-------------

本文标题:Python 高级提升(一)

文章作者:曹永林

发布时间:2018年07月21日 - 09:07

最后更新:2018年07月28日 - 10:07

原始链接:http://jovelin.cn/2018/07/21/Python 高级提升(一)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。